package
{
	import away3d.core.base.data.Vertex;
	
	import com.adobe.utils.AGALMiniAssembler;
	import com.bit101.components.VUISlider;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Graphics;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.display3D.Context3D;
	import flash.display3D.Context3DBlendFactor;
	import flash.display3D.Context3DCompareMode;
	import flash.display3D.Context3DProgramType;
	import flash.display3D.Context3DRenderMode;
	import flash.display3D.Context3DTextureFormat;
	import flash.display3D.Context3DTriangleFace;
	import flash.display3D.Context3DVertexBufferFormat;
	import flash.display3D.IndexBuffer3D;
	import flash.display3D.Program3D;
	import flash.display3D.VertexBuffer3D;
	import flash.display3D.textures.Texture;
	import flash.events.Event;
	import flash.events.TextEvent;
	import flash.geom.Matrix3D;
	import flash.geom.Rectangle;
	import flash.geom.Vector3D;
	import flash.text.TextField;
	import flash.utils.getTimer;
	
	import org.as4game.scene3d.Camera;
	
	/**
	 * 测试用 3D 来渲染 2D 场景的效率
	 */
	[SWF(width='1024',height='600',frameRate='60')]
	public class Test3D extends Sprite
	{
		private var context3D:Context3D;
		
		//
		public function Test3D(){
			super();
			
			//			stage.stage3Ds[0].viewPort = new Rectangle ( 0,0,stage.stageWidth, stage.stageHeight);
			stage.stage3Ds[0].addEventListener( Event.CONTEXT3D_CREATE , onContext3DCreate ); 			
			stage.stage3Ds[0].requestContext3D( Context3DRenderMode.AUTO );
		}
		
		private function onContext3DCreate(event:Event):void{
			
			context3D = stage.stage3Ds[0].context3D;
			
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;			
			stage.addEventListener(Event.RESIZE, onResize);
			stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
			
			init();
			
			onResize( null );
		}
		
		private function onResize(event:Event):void{
			context3D.configureBackBuffer( stage.stageWidth, stage.stageHeight, 1, true);		// 设置背景尺寸
			stage.stage3Ds[0].viewPort = new Rectangle ( 0, 0, stage.stageWidth, stage.stageHeight );	// 设置视口尺寸
			render();
		}
		
		private function onEnterFrame(event:Event):void{
			render();
		}
		
		// =================================================================================
		// game
		
		[Embed(source="../res/walk.png")]
		//		[Embed(source="../res/bowtexture.png")]
		public var walk_class:Class;
		
		static private const ROLE_WIDTH:int = 67;
		static private const ROLE_HEIGHT:int = 91;
		static private const TOTAL_WIDTH:int = 1024;
		static private const TOTAL_HEIGHT:int = 1024;
		static private const FRAME_LENGTH:int = 8;
		static private const NUM_FX:int = 8;
		static private const FX2UY:Array = [2, 5, 0, 4, 1, 6, 3, 7];
		static private const U_UNIT:Number = ROLE_WIDTH / TOTAL_WIDTH;
		static private const V_UNIT:Number = ROLE_HEIGHT / TOTAL_HEIGHT; 
		
		// fps
		private var m_last_time:int;			// 上次更新时间
		private var m_frame_time:int;			// 本帧 - 上帧 的时间间隔
		private var m_fps_count:int;
		private var m_fps_time:int;
		private var m_fps:int;
		
		private function init_time():void{
			m_last_time = getTimer();
			m_fps_time = getTimer();
		}
		private function update_time():void{
			var time:int = getTimer();
			++m_fps_count;
			if( time - m_fps_time > 1000 ){
				m_fps = m_fps_count
				m_fps_count = 0;
				m_fps_time = time;
			}
			m_frame_time = (time - m_last_time);
			m_last_time = time;
		}
		
		// 角色部分
		private var m_role_cnt:int;
		private var m_role_total:int;
		private var m_vt_mem:Vector.<Number> = new Vector.<Number>;		// vx/vy/time
		private var m_vtx_mem:Vector.<Number> = new Vector.<Number>;		// x/y/z, x/y/z, x/y/z, x/y/z
		private var m_idx_mem:Vector.<uint> = new Vector.<uint>;			// 0, 1, 3, 0, 3, 2
		private var m_uv_mem:Vector.<Number> = new Vector.<Number>;		// 0/0, 1/0, 0/1, 1/1
		
		private var m_idx_card:IndexBuffer3D;
		private var m_vtx_card:VertexBuffer3D;
		private var m_uv_card:VertexBuffer3D;
		
		private function init_role(count:int):void{
			
			if( count < 0) count = 0;
			if( count > 10000) count = 10000;
			
			// expand
			if( m_role_total < count ){
				var vt:Number = m_last_time;
				for(var i:int = m_role_total; i<count; i++){
					var v:Number = 100;
					var fx:Number = Math.random();			// [0,1]
					var a:Number = fx * Math.PI * 2;
					var vx:Number = v * Math.cos( a );
					var vy:Number = - v * Math.sin( a );
					var ux:Number = 0;
					var uyi:int = Math.floor( fx * NUM_FX + 0.25 ) % NUM_FX;	// [0,7]
					//					uyi  = 7;
					var uy:Number = FX2UY[uyi] * V_UNIT;
					var id0:int = i * 4;
					
					m_vt_mem.push(vx, vy, vt);
					m_vtx_mem.push(0, 0, 0, ROLE_WIDTH, 0, 0, 0, ROLE_HEIGHT, 0, ROLE_WIDTH, ROLE_HEIGHT, 0);
					m_idx_mem.push(id0+0, id0+1, id0+3, id0+0, id0+3, id0+2); 
					m_uv_mem.push(ux, uy+V_UNIT, ux+U_UNIT, uy+V_UNIT, ux, uy, ux+U_UNIT, uy);
				}
				m_role_total = count;
				
				m_idx_card = context3D.createIndexBuffer( count * 6 );
				m_idx_card.uploadFromVector( m_idx_mem, 0, count * 6 );
				
				m_vtx_card = context3D.createVertexBuffer( count * 4, 3 );
				
				m_uv_card = context3D.createVertexBuffer( count * 4, 2);
			}
			m_role_cnt = count;
		}
		private function update_role():void{
			var right:Number = stage.stageWidth - ROLE_WIDTH
			var bottom:Number = stage.stageHeight - ROLE_HEIGHT;
			var dt:Number = m_frame_time / 1000;
			if( right < 10 ) right = 10;
			if( bottom < 10 ) bottom = 10;
			
			var i:int, ivt:int, ivtx:int, iuv:int;
			var x0:Number, y0:Number, x1:Number, y1:Number, dx:Number, dy:Number, z:Number;
			var vx:Number, vy:Number, vt:Number, fp:Number, fid:int, ft:Number = m_last_time;
			for(; i<m_role_cnt; i++)
			{
				vx = m_vt_mem[ ivt ++ ];
				vy = m_vt_mem[ ivt ++ ];
				vt = m_vt_mem[ ivt ++ ];
				
				x0 = m_vtx_mem[ ivtx + 0 ];
				y0 = m_vtx_mem[ ivtx + 1 ];
				x1 = ( vx * dt + x0 + right ) % right;
				y1 = ( vy * dt + y0 + bottom ) % bottom;
				dx = x1 - x0;
				dy = y1 - y0;
				z = y1 / bottom;			// z = [0, 1]
//				z = -0.5;		// err
//				z = 0;			// ok
//				z = 0.5;		// ok
//				z = 1;			// ok
//				z = 1.5;		// err
				
				m_vtx_mem[ivtx++] += dx;
				m_vtx_mem[ivtx++] += dy;
				m_vtx_mem[ivtx++] = z;
				m_vtx_mem[ivtx++] += dx;
				m_vtx_mem[ivtx++] += dy;
				m_vtx_mem[ivtx++] = z;
				m_vtx_mem[ivtx++] += dx;
				m_vtx_mem[ivtx++] += dy;
				m_vtx_mem[ivtx++] = z;
				m_vtx_mem[ivtx++] += dx;
				m_vtx_mem[ivtx++] += dy;
				m_vtx_mem[ivtx++] = z;
				
				fp = (ft - vt) / 1000;
				fid = (fp * FRAME_LENGTH) % FRAME_LENGTH; 
				x0 = m_uv_mem[ iuv + 0 ];
				x1 = fid * U_UNIT;
				dx = x1 - x0;
				m_uv_mem[ iuv ] += dx; iuv += 2;
				m_uv_mem[ iuv ] += dx; iuv += 2;
				m_uv_mem[ iuv ] += dx; iuv += 2;
				m_uv_mem[ iuv ] += dx; iuv += 2;
			}
			
			m_vtx_card.uploadFromVector( m_vtx_mem, 0, m_role_cnt * 4 );
			m_uv_card.uploadFromVector( m_uv_mem, 0, m_role_cnt * 4 );
		}
		
		// 渲染部分
		private var m_texture:Texture;
		private var m_prog:Program3D;
		private function init_render():void{
			
			// texture
			m_texture = context3D.createTexture( TOTAL_WIDTH, TOTAL_HEIGHT, Context3DTextureFormat.BGRA, false );
			var bitmap:Bitmap = new walk_class();
			m_texture.uploadFromBitmapData( bitmap.bitmapData );
			
			// build program
			var vertexShaderAssembler : AGALMiniAssembler = new AGALMiniAssembler();
			vertexShaderAssembler.assemble( Context3DProgramType.VERTEX,
				"m44 op, va0, vc0\n" +
				"mov v0, va1\n" +
				"" 
			);						
			var fragmentShaderAssembler : AGALMiniAssembler= new AGALMiniAssembler(); 
			fragmentShaderAssembler.assemble( Context3DProgramType.FRAGMENT,
				"mov ft0, v0\n" +
				"tex ft1, ft0, fs0 <2d,clamp,linear>\n"+		 
				"mov oc, ft1\n" +
				""
			);
			m_prog = context3D.createProgram();
			m_prog.upload( vertexShaderAssembler.agalcode, fragmentShaderAssembler.agalcode );
			
		}
		private function update_render():void{
			context3D.clear(0, 0.3);
			if( m_role_cnt ){
				context3D.setProgram( m_prog );
				context3D.setDepthTest(true, Context3DCompareMode.LESS_EQUAL);
				context3D.setBlendFactors ( Context3DBlendFactor.SOURCE_ALPHA, Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA ); 
				
				context3D.setVertexBufferAt(0, m_vtx_card, 0, Context3DVertexBufferFormat.FLOAT_3 );
				context3D.setVertexBufferAt(1, m_uv_card, 0, Context3DVertexBufferFormat.FLOAT_2 );
				context3D.setTextureAt(0, m_texture );
				
//				var matrix:Matrix3D = new Matrix3D();
//				matrix.appendScale( 2 / stage.stageWidth, 2/stage.stageHeight, 1);		// 缩小到舞台尺寸, x=[0,2], y=[0,2], z=0
//				matrix.appendTranslation( -1, -1, 0);
				var matrix:Matrix3D = Camera.makeRectangularProjection( 0, stage.stageWidth, 0, stage.stageHeight, 0, 100);
				
				context3D.setProgramConstantsFromMatrix( Context3DProgramType.VERTEX, 0, matrix, true );
				
				context3D.drawTriangles( m_idx_card, 0, m_role_cnt * 2 );
			}
			context3D.present();
		}
		
		//
		private var m_tf:TextField;
		private var m_sp:Shape;
		private var m_pause:Boolean;
		private function init():void{
			init_time();
			init_role( 100 );
			init_render();
			
			var tf:TextField = m_tf = new TextField;
			tf.multiline = true;
			tf.autoSize = "left";
			tf.wordWrap = false;
			tf.selectable = false;
			tf.addEventListener( TextEvent.LINK, onTextLink);
			addChild( tf );
			
			m_sp = new Shape;
			addChild( m_sp );
		}
		private function render():void{
			update_time();
			if(!m_pause) update_role();
			update_render();
			
			var cnt:int = Math.min( 10, m_role_cnt);
			var ivtx:int;
			var g:Graphics = m_sp.graphics;
			g.clear();
			g.beginFill(0, 0);
			g.lineStyle(1, 0xff0000);
			for(var i:int=0; i< cnt; i++, ivtx+=12){
				var l:Number = m_vtx_mem[ ivtx + 0 ];
				var t:Number = m_vtx_mem[ ivtx + 1 ];
				var r:Number = m_vtx_mem[ ivtx + 9 ];
				var b:Number = m_vtx_mem[ ivtx + 10 ];
				g.drawRect(l, t, r-l, b-t);
			}
			g.endFill();
			
			var str:String = "" +
				"<textformat tabstops='0,100,200,300,400'>" +
				"<font color='#ff0000'>" +
				"FPS: " + m_fps + "\tCnt:" + m_role_cnt + "\tSize:" + stage.stageWidth + "x" + stage.stageHeight + "<br>" +
				"<a href='event:inc'>Inc</a>\t" +
				"<a href='event:dec'>Dec</a>\t" +
				"<a href='event:pause'>" + (m_pause? "Play": "Pause") +"<br>" +
				//						"hit: " + hitstr + "<br" +
				"</font>" +
				"</textformat>" +
				"";
			m_tf.htmlText = str;
		}
		
		private function onTextLink(event:TextEvent):void{
			switch(event.text){
				case "inc":
					init_role( Math.max(1, m_role_cnt * 2) );
					break;
				case "dec":
					init_role( m_role_cnt / 2 );
					break;
				case "pause":
					m_pause = !m_pause;
					break;
			}
		}
	}
}
